﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using lib.Twitter;


namespace lib.JsonParser
{

    // Dummy classes to make some casting look nicer :)
    public class JsonObject : Dictionary<String, Object> { }
    public class JsonArray : List<Object> { }

    // A parser that converts a json formatted string to objects.
    // A json object becomes a Dictionary<String, Object>
    // and a json array becomes a List<Object>
    //
    // Lets call this json object format :)
    //
    public class JsonParser
    {
        static public Object parseJson(String json)
        {
            Object v = null;
            if (json.StartsWith("["))
            {
                parseJsonArray(json.Substring(0), out v);
            }
            else if (json.StartsWith("{"))
            {
                parseJsonObject(json.Substring(0), out v);
            }
            else
            {
                //Console.WriteLine("ERROR PARSING JSON EXPECTED '{' OR '[' GOT '{0}' ", json[0]);
                throw new lib.CustomExceptions.JsonParseError(String.Format("Error parsing json array. Expected ']' got : '{0}'", json[0]));
            }
            return v;
        }

        static private String parseJsonArray(String json, out Object value)
        {
            //Console.WriteLine("ARRAY START");
            JsonArray array = new JsonArray();

            // jump over '['
            json = json.Substring(1); 

            // check for a empty array
            if (json.StartsWith("]"))
            {
                value = array;
                return "";
            }

            //Console.WriteLine("ARRAY VALUE");
            Object v;
            json = parseJsonValue(json, out v);
            array.Add(v);

            // get all objects in the array, [object,object,object] ...
            while (json.StartsWith(","))
            {
                //Console.WriteLine("ARRAY VALUE");
                json = parseJsonValue(json.Substring(1), out v);
                array.Add(v);
            }

            // return object list
            value = array;

            // Check for array end
            if (json.StartsWith("]"))
            {
                //Console.WriteLine("ARRAY END");
                if (json.Length > 1)
                    return json.Substring(1);
                return "";
            }
            //Console.WriteLine("ERROR PARSING ARRAY EXPECTED ']' GOT '{0}'", json[0]);
            throw new lib.CustomExceptions.JsonParseError(String.Format("Error parsing json array. Expected ']' got : '{0}'", json[0]));
        }

        static private String parseJsonObject(String json, out Object value)
        {
            //Console.WriteLine("OBJECT START");
            JsonObject dict = new JsonObject();
            Object v;
            json = json.Substring(1); // jump over '{'
            while (json.StartsWith("\""))
            {
                // string : value
                //Console.WriteLine("OBJECT STRING");                
                json = parseJsonValue(json, out v);
                if (json.StartsWith(":"))
                {
                    String key = (String)v;
                    //Console.WriteLine("{0} : OBJECT VALUE", key);
                    json = parseJsonValue(json.Substring(1), out v);
                    dict.Add(key, v);
                }
                else
                {
                    //Console.WriteLine("ERROR PARSING OBJECT EXPECTED ':' GOT '{0}'", json[0]);
                    throw new lib.CustomExceptions.JsonParseError(String.Format("Error parsing object. Expected ':' got : '{0}'", json[0]));
                }

                if (json.StartsWith(","))
                    json = json.Substring(1);
            }

            if (json.StartsWith("}"))
            {
                //Console.WriteLine("OBJECT END");
                value = dict;
                if (json.Length > 1)
                    return json.Substring(1);

                return "";
            }
            
            //Console.WriteLine("ERROR PARSING OBJECT EXPECTED '}' GOT '{0}'", json[0]);
            throw new lib.CustomExceptions.JsonParseError(String.Format("Error parsing json object. Expected end of object '}' got : '{0}' ", json[0]));
        }

        static private String parseJsonValue(String json, out Object value)
        {
            //Console.Write("VALUE ");
            value = null;
            int i = 0;
            if (json.StartsWith("\""))
            {
                // Parse a string!
 
                // find end of the string
                // check for these two special cases
                // "string\"" - string does not end at first "
                // "string\\" - string does end at first "
                // "string" -  normal string with no special case
                int k = json.IndexOf("\"", 1);
                while (true)
                {
                    // If there is no backslash in front of " then brake
                    // "string"
                    if (!json[k - 1].Equals(Char.Parse("\\")))
                    {
                        break;
                    }
                    // IF there are two backslashes in front of " then brake
                    // "blabla\\"
                    if (json[k - 1].Equals(Char.Parse("\\")) && json[k - 2].Equals(Char.Parse("\\")))
                    {
                        break;
                    }
                    k = json.IndexOf("\"", k + 1);
                }

                value = json.Substring(1, k - 1);
                return json.Substring(k + 1);
            }
            else if (Char.IsDigit(json[0]) || json.StartsWith("-"))
            {
                // Parse a number!
                i = nextIndex(json);
                Double n;
                if (Double.TryParse(json.Substring(0, i), out n))
                {
                    value = n;
                    return json.Substring(i);
                }
                else
                {
                    //Console.WriteLine("ERROR PARSING NUMBER EXPECTED VALID NUMBER GOT '{0}'", json.Substring(0, i));
                    throw new lib.CustomExceptions.JsonParseError(String.Format("Error parsing json value. Expected a valid number got : '{0}'", json.Substring(0, i)));
                }

            }
            else if (json.StartsWith("{"))
            {
                return parseJsonObject(json, out value);
            }
            else if (json.StartsWith("["))
            {
                return parseJsonArray(json, out value);
            }
            else if (json.StartsWith("true"))
            {
                value = true;
                i = nextIndex(json);
            }
            else if (json.StartsWith("false"))
            {
                value = false;
                i = nextIndex(json);
            }
            else if (json.StartsWith("null"))
            {
                value = null;
                i = nextIndex(json);
            }
            else
            {
                //Console.WriteLine("ERROR PARSING VALUE GOT '{0}'", json[0]);
                throw new lib.CustomExceptions.JsonParseError(String.Format("Error parsing json value. Expected begining of a value got : '{0}'",json[0]));
            }

            return json.Substring(i);
        }

        static private int nextIndex(String json)
        {
            int i = -1;
            int k = json.IndexOf(",");
            if (k == -1)
                k = 999999;
            int j = json.IndexOf("}");
            if (j == -1)
                j = 999999;
            int l = json.IndexOf("]");
            if (l == -1)
                l = 999999;

            if (k < j && k < l)
                i = k;
            else if (j < k && j < l)
                i = j;
            else if (l < k && l < j)
                i = l;

            if (i == -1)
            {
                //Console.WriteLine("ERROR FINDING NEXT INDEX");
                //Console.WriteLine("k : {0} - j : {1} - l : {2} = i : {3}", k, j, l, i);
                throw new lib.CustomExceptions.JsonParseError(String.Format("Error finding next index - k : {0}, j : {1}, l : {2}, i : {3}", k,j,l,i));
            }
            
            return i;
        }

    }
}
